// Copyright 2013 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "ui/message_center/message_center_impl.h"

#include <utility>

#include "base/bind.h"
#include "base/macros.h"
#include "base/message_loop/message_loop.h"
#include "base/run_loop.h"
#include "base/strings/utf_string_conversions.h"
#include "build/build_config.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "ui/gfx/canvas.h"
#include "ui/gfx/geometry/size.h"
#include "ui/message_center/message_center.h"
#include "ui/message_center/message_center_types.h"
#include "ui/message_center/notification_blocker.h"
#include "ui/message_center/notification_types.h"
#include "ui/message_center/notifier_settings.h"

using base::UTF8ToUTF16;

namespace message_center {

class MessageCenterImplTest : public testing::Test,
                              public MessageCenterObserver {
public:
    MessageCenterImplTest() { }

    void SetUp() override
    {
        MessageCenter::Initialize();
        message_center_ = MessageCenter::Get();
        loop_.reset(new base::MessageLoop);
        run_loop_.reset(new base::RunLoop());
        closure_ = run_loop_->QuitClosure();
    }

    void TearDown() override
    {
        run_loop_.reset();
        loop_.reset();
        message_center_ = NULL;
        MessageCenter::Shutdown();
    }

    MessageCenter* message_center() const { return message_center_; }
    NotifierSettingsObserver* notifier_settings_observer() const
    {
        return static_cast<NotifierSettingsObserver*>(message_center_impl());
    }
    MessageCenterImpl* message_center_impl() const
    {
        return reinterpret_cast<MessageCenterImpl*>(message_center_);
    }

    base::RunLoop* run_loop() const { return run_loop_.get(); }
    base::Closure closure() const { return closure_; }

protected:
    Notification* CreateSimpleNotification(const std::string& id)
    {
        return CreateNotificationWithNotifierId(
            id,
            "app1",
            NOTIFICATION_TYPE_SIMPLE);
    }

    Notification* CreateSimpleNotificationWithNotifierId(
        const std::string& id, const std::string& notifier_id)
    {
        return CreateNotificationWithNotifierId(
            id,
            notifier_id,
            NOTIFICATION_TYPE_SIMPLE);
    }

    Notification* CreateNotification(const std::string& id,
        message_center::NotificationType type)
    {
        return CreateNotificationWithNotifierId(id, "app1", type);
    }

    Notification* CreateNotificationWithNotifierId(
        const std::string& id,
        const std::string& notifier_id,
        message_center::NotificationType type)
    {
        RichNotificationData optional_fields;
        optional_fields.buttons.push_back(ButtonInfo(UTF8ToUTF16("foo")));
        optional_fields.buttons.push_back(ButtonInfo(UTF8ToUTF16("foo")));
        return new Notification(type, id, UTF8ToUTF16("title"), UTF8ToUTF16(id),
            gfx::Image() /* icon */,
            base::string16() /* display_source */, GURL(),
            NotifierId(NotifierId::APPLICATION, notifier_id),
            optional_fields, NULL);
    }

    void ForceNotificationFlush(const std::string& id)
    {
        message_center()->ForceNotificationFlush(id);
    }

private:
    MessageCenter* message_center_;
    scoped_ptr<base::MessageLoop> loop_;
    scoped_ptr<base::RunLoop> run_loop_;
    base::Closure closure_;

    DISALLOW_COPY_AND_ASSIGN(MessageCenterImplTest);
};

class MessageCenterImplTestWithChangeQueue : public MessageCenterImplTest {
public:
    MessageCenterImplTestWithChangeQueue() { }
    ~MessageCenterImplTestWithChangeQueue() override { }

    void SetUp() override
    {
        MessageCenterImplTest::SetUp();
        message_center()->EnableChangeQueueForTest(true);
    }

private:
    DISALLOW_COPY_AND_ASSIGN(MessageCenterImplTestWithChangeQueue);
};

class MessageCenterImplTestWithoutChangeQueue : public MessageCenterImplTest {
public:
    MessageCenterImplTestWithoutChangeQueue() { }
    ~MessageCenterImplTestWithoutChangeQueue() override { }

    void SetUp() override
    {
        MessageCenterImplTest::SetUp();
        message_center()->EnableChangeQueueForTest(false);
    }

private:
    DISALLOW_COPY_AND_ASSIGN(MessageCenterImplTestWithoutChangeQueue);
};

namespace {

    class ToggledNotificationBlocker : public NotificationBlocker {
    public:
        explicit ToggledNotificationBlocker(MessageCenter* message_center)
            : NotificationBlocker(message_center)
            , notifications_enabled_(true)
        {
        }
        ~ToggledNotificationBlocker() override { }

        void SetNotificationsEnabled(bool enabled)
        {
            if (notifications_enabled_ != enabled) {
                notifications_enabled_ = enabled;
                NotifyBlockingStateChanged();
            }
        }

        // NotificationBlocker overrides:
        bool ShouldShowNotificationAsPopup(
            const message_center::NotifierId& notifier_id) const override
        {
            return notifications_enabled_;
        }

    private:
        bool notifications_enabled_;

        DISALLOW_COPY_AND_ASSIGN(ToggledNotificationBlocker);
    };

    class PopupNotificationBlocker : public ToggledNotificationBlocker {
    public:
        PopupNotificationBlocker(MessageCenter* message_center,
            const NotifierId& allowed_notifier)
            : ToggledNotificationBlocker(message_center)
            , allowed_notifier_(allowed_notifier)
        {
        }
        ~PopupNotificationBlocker() override { }

        // NotificationBlocker overrides:
        bool ShouldShowNotificationAsPopup(
            const NotifierId& notifier_id) const override
        {
            return (notifier_id == allowed_notifier_) || ToggledNotificationBlocker::ShouldShowNotificationAsPopup(notifier_id);
        }

    private:
        NotifierId allowed_notifier_;

        DISALLOW_COPY_AND_ASSIGN(PopupNotificationBlocker);
    };

    class TotalNotificationBlocker : public PopupNotificationBlocker {
    public:
        TotalNotificationBlocker(MessageCenter* message_center,
            const NotifierId& allowed_notifier)
            : PopupNotificationBlocker(message_center, allowed_notifier)
        {
        }
        ~TotalNotificationBlocker() override { }

        // NotificationBlocker overrides:
        bool ShouldShowNotification(const NotifierId& notifier_id) const override
        {
            return ShouldShowNotificationAsPopup(notifier_id);
        }

    private:
        DISALLOW_COPY_AND_ASSIGN(TotalNotificationBlocker);
    };

    bool PopupNotificationsContain(
        const NotificationList::PopupNotifications& popups,
        const std::string& id)
    {
        for (NotificationList::PopupNotifications::const_iterator iter = popups.begin(); iter != popups.end(); ++iter) {
            if ((*iter)->id() == id)
                return true;
        }
        return false;
    }

    // Right now, MessageCenter::HasNotification() returns regardless of blockers.
    bool NotificationsContain(
        const NotificationList::Notifications& notifications,
        const std::string& id)
    {
        for (NotificationList::Notifications::const_iterator iter = notifications.begin(); iter != notifications.end(); ++iter) {
            if ((*iter)->id() == id)
                return true;
        }
        return false;
    }

} // namespace

namespace internal {

    class MockPopupTimersController : public PopupTimersController {
    public:
        MockPopupTimersController(MessageCenter* message_center,
            base::Closure quit_closure)
            : PopupTimersController(message_center)
            , timer_finished_(false)
            , quit_closure_(quit_closure)
        {
        }
        ~MockPopupTimersController() override { }

        void TimerFinished(const std::string& id) override
        {
            base::MessageLoop::current()->PostTask(FROM_HERE, quit_closure_);
            timer_finished_ = true;
            last_id_ = id;
        }

        bool timer_finished() const { return timer_finished_; }
        const std::string& last_id() const { return last_id_; }

    private:
        bool timer_finished_;
        std::string last_id_;
        base::Closure quit_closure_;
    };

    TEST_F(MessageCenterImplTest, PopupTimersEmptyController)
    {
        scoped_ptr<PopupTimersController> popup_timers_controller = make_scoped_ptr(new PopupTimersController(message_center()));

        // Test that all functions succed without any timers created.
        popup_timers_controller->PauseAll();
        popup_timers_controller->StartAll();
        popup_timers_controller->CancelAll();
        popup_timers_controller->TimerFinished("unknown");
        popup_timers_controller->PauseTimer("unknown");
        popup_timers_controller->CancelTimer("unknown");
    }

    TEST_F(MessageCenterImplTest, PopupTimersControllerStartTimer)
    {
        scoped_ptr<MockPopupTimersController> popup_timers_controller = make_scoped_ptr(
            new MockPopupTimersController(message_center(), closure()));
        popup_timers_controller->StartTimer("test",
            base::TimeDelta::FromMilliseconds(1));
        run_loop()->Run();
        EXPECT_TRUE(popup_timers_controller->timer_finished());
    }

    TEST_F(MessageCenterImplTest, PopupTimersControllerPauseTimer)
    {
        scoped_ptr<MockPopupTimersController> popup_timers_controller = make_scoped_ptr(
            new MockPopupTimersController(message_center(), closure()));
        popup_timers_controller->StartTimer("test",
            base::TimeDelta::FromMilliseconds(1));
        popup_timers_controller->PauseTimer("test");
        run_loop()->RunUntilIdle();

        EXPECT_FALSE(popup_timers_controller->timer_finished());
    }

    TEST_F(MessageCenterImplTest, PopupTimersControllerCancelTimer)
    {
        scoped_ptr<MockPopupTimersController> popup_timers_controller = make_scoped_ptr(
            new MockPopupTimersController(message_center(), closure()));
        popup_timers_controller->StartTimer("test",
            base::TimeDelta::FromMilliseconds(1));
        popup_timers_controller->CancelTimer("test");
        run_loop()->RunUntilIdle();

        EXPECT_FALSE(popup_timers_controller->timer_finished());
    }

    TEST_F(MessageCenterImplTest, PopupTimersControllerPauseAllTimers)
    {
        scoped_ptr<MockPopupTimersController> popup_timers_controller = make_scoped_ptr(
            new MockPopupTimersController(message_center(), closure()));
        popup_timers_controller->StartTimer("test",
            base::TimeDelta::FromMilliseconds(1));
        popup_timers_controller->PauseAll();
        run_loop()->RunUntilIdle();

        EXPECT_FALSE(popup_timers_controller->timer_finished());
    }

    TEST_F(MessageCenterImplTest, PopupTimersControllerStartAllTimers)
    {
        scoped_ptr<MockPopupTimersController> popup_timers_controller = make_scoped_ptr(
            new MockPopupTimersController(message_center(), closure()));
        popup_timers_controller->StartTimer("test",
            base::TimeDelta::FromMilliseconds(1));
        popup_timers_controller->PauseAll();
        popup_timers_controller->StartAll();
        run_loop()->Run();

        EXPECT_TRUE(popup_timers_controller->timer_finished());
    }

    TEST_F(MessageCenterImplTest, PopupTimersControllerStartMultipleTimers)
    {
        scoped_ptr<MockPopupTimersController> popup_timers_controller = make_scoped_ptr(
            new MockPopupTimersController(message_center(), closure()));
        popup_timers_controller->StartTimer("test",
            base::TimeDelta::FromMilliseconds(5));
        popup_timers_controller->StartTimer("test2",
            base::TimeDelta::FromMilliseconds(1));
        popup_timers_controller->StartTimer("test3",
            base::TimeDelta::FromMilliseconds(3));
        popup_timers_controller->PauseAll();
        popup_timers_controller->StartAll();
        run_loop()->Run();

        EXPECT_EQ(popup_timers_controller->last_id(), "test2");
        EXPECT_TRUE(popup_timers_controller->timer_finished());
    }

    TEST_F(MessageCenterImplTest, PopupTimersControllerStartMultipleTimersPause)
    {
        scoped_ptr<MockPopupTimersController> popup_timers_controller = make_scoped_ptr(
            new MockPopupTimersController(message_center(), closure()));
        popup_timers_controller->StartTimer("test",
            base::TimeDelta::FromMilliseconds(5));
        popup_timers_controller->StartTimer("test2",
            base::TimeDelta::FromMilliseconds(1));
        popup_timers_controller->StartTimer("test3",
            base::TimeDelta::FromMilliseconds(3));
        popup_timers_controller->PauseTimer("test2");

        run_loop()->Run();

        EXPECT_EQ(popup_timers_controller->last_id(), "test3");
        EXPECT_TRUE(popup_timers_controller->timer_finished());
    }

    TEST_F(MessageCenterImplTest, PopupTimersControllerResetTimer)
    {
        scoped_ptr<MockPopupTimersController> popup_timers_controller = make_scoped_ptr(
            new MockPopupTimersController(message_center(), closure()));
        popup_timers_controller->StartTimer("test",
            base::TimeDelta::FromMilliseconds(5));
        popup_timers_controller->StartTimer("test2",
            base::TimeDelta::FromMilliseconds(1));
        popup_timers_controller->StartTimer("test3",
            base::TimeDelta::FromMilliseconds(3));
        popup_timers_controller->PauseTimer("test2");
        popup_timers_controller->ResetTimer("test",
            base::TimeDelta::FromMilliseconds(2));

        run_loop()->Run();

        EXPECT_EQ(popup_timers_controller->last_id(), "test");
        EXPECT_TRUE(popup_timers_controller->timer_finished());
    }

    TEST_F(MessageCenterImplTest, NotificationBlocker)
    {
        NotifierId notifier_id(NotifierId::APPLICATION, "app1");
        // Multiple blockers to verify the case that one blocker blocks but another
        // doesn't.
        ToggledNotificationBlocker blocker1(message_center());
        ToggledNotificationBlocker blocker2(message_center());

        message_center()->AddNotification(scoped_ptr<Notification>(
            new Notification(NOTIFICATION_TYPE_SIMPLE, "id1", UTF8ToUTF16("title"),
                UTF8ToUTF16("message"), gfx::Image() /* icon */,
                base::string16() /* display_source */, GURL(),
                notifier_id, RichNotificationData(), NULL)));
        message_center()->AddNotification(scoped_ptr<Notification>(
            new Notification(NOTIFICATION_TYPE_SIMPLE, "id2", UTF8ToUTF16("title"),
                UTF8ToUTF16("message"), gfx::Image() /* icon */,
                base::string16() /* display_source */, GURL(),
                notifier_id, RichNotificationData(), NULL)));
        EXPECT_EQ(2u, message_center()->GetPopupNotifications().size());
        EXPECT_EQ(2u, message_center()->GetVisibleNotifications().size());

        // Block all notifications. All popups are gone and message center should be
        // hidden.
        blocker1.SetNotificationsEnabled(false);
        EXPECT_TRUE(message_center()->GetPopupNotifications().empty());
        EXPECT_EQ(2u, message_center()->GetVisibleNotifications().size());

        // Updates |blocker2| state, which doesn't affect the global state.
        blocker2.SetNotificationsEnabled(false);
        EXPECT_TRUE(message_center()->GetPopupNotifications().empty());
        EXPECT_EQ(2u, message_center()->GetVisibleNotifications().size());

        blocker2.SetNotificationsEnabled(true);
        EXPECT_TRUE(message_center()->GetPopupNotifications().empty());
        EXPECT_EQ(2u, message_center()->GetVisibleNotifications().size());

        // If |blocker2| blocks, then unblocking blocker1 doesn't change the global
        // state.
        blocker2.SetNotificationsEnabled(false);
        blocker1.SetNotificationsEnabled(true);
        EXPECT_TRUE(message_center()->GetPopupNotifications().empty());
        EXPECT_EQ(2u, message_center()->GetVisibleNotifications().size());

        // Unblock both blockers, which recovers the global state, but the popups
        // aren't shown.
        blocker2.SetNotificationsEnabled(true);
        EXPECT_TRUE(message_center()->GetPopupNotifications().empty());
        EXPECT_EQ(2u, message_center()->GetVisibleNotifications().size());
    }

    TEST_F(MessageCenterImplTest, NotificationsDuringBlocked)
    {
        NotifierId notifier_id(NotifierId::APPLICATION, "app1");
        ToggledNotificationBlocker blocker(message_center());

        message_center()->AddNotification(scoped_ptr<Notification>(
            new Notification(NOTIFICATION_TYPE_SIMPLE, "id1", UTF8ToUTF16("title"),
                UTF8ToUTF16("message"), gfx::Image() /* icon */,
                base::string16() /* display_source */, GURL(),
                notifier_id, RichNotificationData(), NULL)));
        EXPECT_EQ(1u, message_center()->GetPopupNotifications().size());
        EXPECT_EQ(1u, message_center()->GetVisibleNotifications().size());

        // Create a notification during blocked. Still no popups.
        blocker.SetNotificationsEnabled(false);
        message_center()->AddNotification(scoped_ptr<Notification>(
            new Notification(NOTIFICATION_TYPE_SIMPLE, "id2", UTF8ToUTF16("title"),
                UTF8ToUTF16("message"), gfx::Image() /* icon */,
                base::string16() /* display_source */, GURL(),
                notifier_id, RichNotificationData(), NULL)));
        EXPECT_TRUE(message_center()->GetPopupNotifications().empty());
        EXPECT_EQ(2u, message_center()->GetVisibleNotifications().size());

        // Unblock notifications, the id1 should appear as a popup.
        blocker.SetNotificationsEnabled(true);
        NotificationList::PopupNotifications popups = message_center()->GetPopupNotifications();
        EXPECT_EQ(1u, popups.size());
        EXPECT_TRUE(PopupNotificationsContain(popups, "id2"));
        EXPECT_EQ(2u, message_center()->GetVisibleNotifications().size());
    }

    // Similar to other blocker cases but this test case allows |notifier_id2| even
    // in blocked.
    TEST_F(MessageCenterImplTest, NotificationBlockerAllowsPopups)
    {
        NotifierId notifier_id1(NotifierId::APPLICATION, "app1");
        NotifierId notifier_id2(NotifierId::APPLICATION, "app2");
        PopupNotificationBlocker blocker(message_center(), notifier_id2);

        message_center()->AddNotification(scoped_ptr<Notification>(
            new Notification(NOTIFICATION_TYPE_SIMPLE, "id1", UTF8ToUTF16("title"),
                UTF8ToUTF16("message"), gfx::Image() /* icon */,
                base::string16() /* display_source */, GURL(),
                notifier_id1, RichNotificationData(), NULL)));
        message_center()->AddNotification(scoped_ptr<Notification>(
            new Notification(NOTIFICATION_TYPE_SIMPLE, "id2", UTF8ToUTF16("title"),
                UTF8ToUTF16("message"), gfx::Image() /* icon */,
                base::string16() /* display_source */, GURL(),
                notifier_id2, RichNotificationData(), NULL)));

        // "id1" is closed but "id2" is still visible as a popup.
        blocker.SetNotificationsEnabled(false);
        NotificationList::PopupNotifications popups = message_center()->GetPopupNotifications();
        EXPECT_EQ(1u, popups.size());
        EXPECT_TRUE(PopupNotificationsContain(popups, "id2"));
        EXPECT_EQ(2u, message_center()->GetVisibleNotifications().size());

        message_center()->AddNotification(scoped_ptr<Notification>(
            new Notification(NOTIFICATION_TYPE_SIMPLE, "id3", UTF8ToUTF16("title"),
                UTF8ToUTF16("message"), gfx::Image() /* icon */,
                base::string16() /* display_source */, GURL(),
                notifier_id1, RichNotificationData(), NULL)));
        message_center()->AddNotification(scoped_ptr<Notification>(
            new Notification(NOTIFICATION_TYPE_SIMPLE, "id4", UTF8ToUTF16("title"),
                UTF8ToUTF16("message"), gfx::Image() /* icon */,
                base::string16() /* display_source */, GURL(),
                notifier_id2, RichNotificationData(), NULL)));
        popups = message_center()->GetPopupNotifications();
        EXPECT_EQ(2u, popups.size());
        EXPECT_TRUE(PopupNotificationsContain(popups, "id2"));
        EXPECT_TRUE(PopupNotificationsContain(popups, "id4"));
        EXPECT_EQ(4u, message_center()->GetVisibleNotifications().size());

        blocker.SetNotificationsEnabled(true);
        popups = message_center()->GetPopupNotifications();
        EXPECT_EQ(3u, popups.size());
        EXPECT_TRUE(PopupNotificationsContain(popups, "id2"));
        EXPECT_TRUE(PopupNotificationsContain(popups, "id3"));
        EXPECT_TRUE(PopupNotificationsContain(popups, "id4"));
        EXPECT_EQ(4u, message_center()->GetVisibleNotifications().size());
    }

    // TotalNotificationBlocker suppresses showing notifications even from the list.
    // This would provide the feature to 'separated' message centers per-profile for
    // ChromeOS multi-login.
    TEST_F(MessageCenterImplTest, TotalNotificationBlocker)
    {
        NotifierId notifier_id1(NotifierId::APPLICATION, "app1");
        NotifierId notifier_id2(NotifierId::APPLICATION, "app2");
        TotalNotificationBlocker blocker(message_center(), notifier_id2);

        message_center()->AddNotification(scoped_ptr<Notification>(
            new Notification(NOTIFICATION_TYPE_SIMPLE, "id1", UTF8ToUTF16("title"),
                UTF8ToUTF16("message"), gfx::Image() /* icon */,
                base::string16() /* display_source */, GURL(),
                notifier_id1, RichNotificationData(), NULL)));
        message_center()->AddNotification(scoped_ptr<Notification>(
            new Notification(NOTIFICATION_TYPE_SIMPLE, "id2", UTF8ToUTF16("title"),
                UTF8ToUTF16("message"), gfx::Image() /* icon */,
                base::string16() /* display_source */, GURL(),
                notifier_id2, RichNotificationData(), NULL)));

        // "id1" becomes invisible while "id2" is still visible.
        blocker.SetNotificationsEnabled(false);
        EXPECT_EQ(1u, message_center()->NotificationCount());
        NotificationList::Notifications notifications = message_center()->GetVisibleNotifications();
        EXPECT_FALSE(NotificationsContain(notifications, "id1"));
        EXPECT_TRUE(NotificationsContain(notifications, "id2"));

        message_center()->AddNotification(scoped_ptr<Notification>(
            new Notification(NOTIFICATION_TYPE_SIMPLE, "id3", UTF8ToUTF16("title"),
                UTF8ToUTF16("message"), gfx::Image() /* icon */,
                base::string16() /* display_source */, GURL(),
                notifier_id1, RichNotificationData(), NULL)));
        message_center()->AddNotification(scoped_ptr<Notification>(
            new Notification(NOTIFICATION_TYPE_SIMPLE, "id4", UTF8ToUTF16("title"),
                UTF8ToUTF16("message"), gfx::Image() /* icon */,
                base::string16() /* display_source */, GURL(),
                notifier_id2, RichNotificationData(), NULL)));
        EXPECT_EQ(2u, message_center()->NotificationCount());
        notifications = message_center()->GetVisibleNotifications();
        EXPECT_FALSE(NotificationsContain(notifications, "id1"));
        EXPECT_TRUE(NotificationsContain(notifications, "id2"));
        EXPECT_FALSE(NotificationsContain(notifications, "id3"));
        EXPECT_TRUE(NotificationsContain(notifications, "id4"));

        blocker.SetNotificationsEnabled(true);
        EXPECT_EQ(4u, message_center()->NotificationCount());
        notifications = message_center()->GetVisibleNotifications();
        EXPECT_TRUE(NotificationsContain(notifications, "id1"));
        EXPECT_TRUE(NotificationsContain(notifications, "id2"));
        EXPECT_TRUE(NotificationsContain(notifications, "id3"));
        EXPECT_TRUE(NotificationsContain(notifications, "id4"));

        // RemoveAllVisibleNotifications should remove just visible notifications.
        blocker.SetNotificationsEnabled(false);
        message_center()->RemoveAllVisibleNotifications(false /* by_user */);
        EXPECT_EQ(0u, message_center()->NotificationCount());
        blocker.SetNotificationsEnabled(true);
        EXPECT_EQ(2u, message_center()->NotificationCount());
        notifications = message_center()->GetVisibleNotifications();
        EXPECT_TRUE(NotificationsContain(notifications, "id1"));
        EXPECT_FALSE(NotificationsContain(notifications, "id2"));
        EXPECT_TRUE(NotificationsContain(notifications, "id3"));
        EXPECT_FALSE(NotificationsContain(notifications, "id4"));

        // And RemoveAllNotifications should remove all.
        blocker.SetNotificationsEnabled(false);
        message_center()->RemoveAllNotifications(false /* by_user */);
        EXPECT_EQ(0u, message_center()->NotificationCount());
    }

#if defined(OS_CHROMEOS)
    TEST_F(MessageCenterImplTest, CachedUnreadCount)
    {
        message_center()->AddNotification(
            scoped_ptr<Notification>(CreateSimpleNotification("id1")));
        message_center()->AddNotification(
            scoped_ptr<Notification>(CreateSimpleNotification("id2")));
        message_center()->AddNotification(
            scoped_ptr<Notification>(CreateSimpleNotification("id3")));
        ASSERT_EQ(3u, message_center()->UnreadNotificationCount());

        // Mark 'displayed' on all notifications by using for-loop. This shouldn't
        // recreate |notifications| inside of the loop.
        const NotificationList::Notifications& notifications = message_center()->GetVisibleNotifications();
        for (NotificationList::Notifications::const_iterator iter = notifications.begin(); iter != notifications.end(); ++iter) {
            message_center()->DisplayedNotification(
                (*iter)->id(), message_center::DISPLAY_SOURCE_MESSAGE_CENTER);
        }
        EXPECT_EQ(0u, message_center()->UnreadNotificationCount());

        // Imitate the timeout, which recovers the unread count. Again, this shouldn't
        // recreate |notifications| inside of the loop.
        for (NotificationList::Notifications::const_iterator iter = notifications.begin(); iter != notifications.end(); ++iter) {
            message_center()->MarkSinglePopupAsShown((*iter)->id(), false);
        }
        EXPECT_EQ(3u, message_center()->UnreadNotificationCount());

        // Opening the message center will reset the unread count.
        message_center()->SetVisibility(VISIBILITY_MESSAGE_CENTER);
        EXPECT_EQ(0u, message_center()->UnreadNotificationCount());
    }
#endif // OS_CHROMEOS

    TEST_F(MessageCenterImplTest, ForceNotificationFlush_InconsistentUpdate)
    {
        std::string id1("id1");
        std::string id2("id2");
        std::string id3("id3");

        message_center()->SetVisibility(VISIBILITY_MESSAGE_CENTER);

        // Add -> Update (with ID change)
        scoped_ptr<Notification> notification(CreateSimpleNotification(id1));
        message_center()->AddNotification(std::move(notification));
        notification.reset(CreateSimpleNotification(id2));
        message_center()->UpdateNotification(id1, std::move(notification));

        // Add (although the same ID exists) -> Update (with ID change) -> Remove
        notification.reset(CreateSimpleNotification(id2));
        message_center()->AddNotification(std::move(notification));
        notification.reset(CreateSimpleNotification(id3));
        message_center()->UpdateNotification(id2, std::move(notification));
        message_center()->RemoveNotification(id3, false);

        // Remove (although the ID has already removed)
        message_center()->RemoveNotification(id3, false);

        // Notification is not added since the message center has opened.
        ASSERT_EQ(0u, message_center()->NotificationCount());

        // Forced to update.
        ForceNotificationFlush(id3);

        // Confirms the chagnes are applied.
        ASSERT_EQ(0u, message_center()->NotificationCount());
    }

    TEST_F(MessageCenterImplTest, DisableNotificationsByNotifier)
    {
        ASSERT_EQ(0u, message_center()->NotificationCount());
        message_center()->AddNotification(
            scoped_ptr<Notification>(
                CreateSimpleNotificationWithNotifierId("id1-1", "app1")));
        message_center()->AddNotification(
            scoped_ptr<Notification>(
                CreateSimpleNotificationWithNotifierId("id1-2", "app1")));
        message_center()->AddNotification(
            scoped_ptr<Notification>(
                CreateSimpleNotificationWithNotifierId("id2-1", "app2")));
        message_center()->AddNotification(
            scoped_ptr<Notification>(
                CreateSimpleNotificationWithNotifierId("id2-2", "app2")));
        message_center()->AddNotification(
            scoped_ptr<Notification>(
                CreateSimpleNotificationWithNotifierId("id2-3", "app2")));
        ASSERT_EQ(5u, message_center()->NotificationCount());

        // Removing all of app1's notifications should only leave app2's.
        message_center()->DisableNotificationsByNotifier(
            NotifierId(NotifierId::APPLICATION, "app1"));
        ASSERT_EQ(3u, message_center()->NotificationCount());

        // Now we remove the remaining notifications.
        message_center()->DisableNotificationsByNotifier(
            NotifierId(NotifierId::APPLICATION, "app2"));
        ASSERT_EQ(0u, message_center()->NotificationCount());
    }

    TEST_F(MessageCenterImplTest, NotifierEnabledChanged)
    {
        ASSERT_EQ(0u, message_center()->NotificationCount());
        message_center()->AddNotification(
            scoped_ptr<Notification>(
                CreateSimpleNotificationWithNotifierId("id1-1", "app1")));
        message_center()->AddNotification(
            scoped_ptr<Notification>(
                CreateSimpleNotificationWithNotifierId("id1-2", "app1")));
        message_center()->AddNotification(
            scoped_ptr<Notification>(
                CreateSimpleNotificationWithNotifierId("id1-3", "app1")));
        message_center()->AddNotification(
            scoped_ptr<Notification>(
                CreateSimpleNotificationWithNotifierId("id2-1", "app2")));
        message_center()->AddNotification(
            scoped_ptr<Notification>(
                CreateSimpleNotificationWithNotifierId("id2-2", "app2")));
        message_center()->AddNotification(
            scoped_ptr<Notification>(
                CreateSimpleNotificationWithNotifierId("id2-3", "app2")));
        message_center()->AddNotification(
            scoped_ptr<Notification>(
                CreateSimpleNotificationWithNotifierId("id2-4", "app2")));
        message_center()->AddNotification(
            scoped_ptr<Notification>(
                CreateSimpleNotificationWithNotifierId("id2-5", "app2")));
        ASSERT_EQ(8u, message_center()->NotificationCount());

        // Enabling an extension should have no effect on the count.
        notifier_settings_observer()->NotifierEnabledChanged(
            NotifierId(NotifierId::APPLICATION, "app1"), true);
        ASSERT_EQ(8u, message_center()->NotificationCount());

        // Removing all of app2's notifications should only leave app1's.
        notifier_settings_observer()->NotifierEnabledChanged(
            NotifierId(NotifierId::APPLICATION, "app2"), false);
        ASSERT_EQ(3u, message_center()->NotificationCount());

        // Removal operations should be idempotent.
        notifier_settings_observer()->NotifierEnabledChanged(
            NotifierId(NotifierId::APPLICATION, "app2"), false);
        ASSERT_EQ(3u, message_center()->NotificationCount());

        // Now we remove the remaining notifications.
        notifier_settings_observer()->NotifierEnabledChanged(
            NotifierId(NotifierId::APPLICATION, "app1"), false);
        ASSERT_EQ(0u, message_center()->NotificationCount());
    }

    TEST_F(MessageCenterImplTestWithChangeQueue, QueueUpdatesWithCenterVisible)
    {
        std::string id("id1");
        std::string id2("id2");
        NotifierId notifier_id1(NotifierId::APPLICATION, "app1");

        // First, add and update a notification to ensure updates happen
        // normally.
        scoped_ptr<Notification> notification(CreateSimpleNotification(id));
        message_center()->AddNotification(std::move(notification));
        notification.reset(CreateSimpleNotification(id2));
        message_center()->UpdateNotification(id, std::move(notification));
        EXPECT_TRUE(message_center()->FindVisibleNotificationById(id2));
        EXPECT_FALSE(message_center()->FindVisibleNotificationById(id));

        // Then open the message center.
        message_center()->SetVisibility(VISIBILITY_MESSAGE_CENTER);

        // Then update a notification; nothing should have happened.
        notification.reset(CreateSimpleNotification(id));
        message_center()->UpdateNotification(id2, std::move(notification));
        EXPECT_TRUE(message_center()->FindVisibleNotificationById(id2));
        EXPECT_FALSE(message_center()->FindVisibleNotificationById(id));

        // Close the message center; then the update should have propagated.
        message_center()->SetVisibility(VISIBILITY_TRANSIENT);
        EXPECT_FALSE(message_center()->FindVisibleNotificationById(id2));
        EXPECT_TRUE(message_center()->FindVisibleNotificationById(id));
    }

    TEST_F(MessageCenterImplTestWithChangeQueue, ComplexQueueing)
    {
        std::string ids[6] = { "0", "1", "2", "3", "4p", "5" };
        NotifierId notifier_id1(NotifierId::APPLICATION, "app1");

        scoped_ptr<Notification> notification;
        // Add some notifications
        int i = 0;
        for (; i < 3; i++) {
            notification.reset(CreateSimpleNotification(ids[i]));
            message_center()->AddNotification(std::move(notification));
        }
        for (i = 0; i < 3; i++) {
            EXPECT_TRUE(message_center()->FindVisibleNotificationById(ids[i]));
        }
        for (; i < 6; i++) {
            EXPECT_FALSE(message_center()->FindVisibleNotificationById(ids[i]));
        }

        notification.reset(CreateNotification(ids[4], NOTIFICATION_TYPE_PROGRESS));
        message_center()->AddNotification(std::move(notification));

        // Now start queueing.
        // NL: ["0", "1", "2", "4p"]
        message_center()->SetVisibility(VISIBILITY_MESSAGE_CENTER);

        // This should update notification "1" to have id "3".
        notification.reset(CreateSimpleNotification(ids[3]));
        message_center()->UpdateNotification(ids[1], std::move(notification));

        // Change the ID: "4p" -> "5", "5" -> "1", "1" -> "4p". They shouldn't
        // override the previous change ("1" -> "3") nor the next one ("3" -> "5").
        notification.reset(CreateSimpleNotification(ids[5]));
        message_center()->UpdateNotification(ids[4], std::move(notification));
        notification.reset(CreateSimpleNotification(ids[1]));
        message_center()->UpdateNotification(ids[5], std::move(notification));
        notification.reset(CreateNotification(ids[4], NOTIFICATION_TYPE_PROGRESS));
        message_center()->UpdateNotification(ids[1], std::move(notification));

        // This should update notification "3" to "5" after we go TRANSIENT.
        notification.reset(CreateSimpleNotification(ids[5]));
        message_center()->UpdateNotification(ids[3], std::move(notification));

        // This should create a new "3", that doesn't overwrite the update to 3
        // before.
        notification.reset(CreateSimpleNotification(ids[3]));
        message_center()->AddNotification(std::move(notification));

        // The NL should still be the same: ["0", "1", "2", "4p"]
        EXPECT_TRUE(message_center()->FindVisibleNotificationById(ids[0]));
        EXPECT_TRUE(message_center()->FindVisibleNotificationById(ids[1]));
        EXPECT_TRUE(message_center()->FindVisibleNotificationById(ids[2]));
        EXPECT_FALSE(message_center()->FindVisibleNotificationById(ids[3]));
        EXPECT_TRUE(message_center()->FindVisibleNotificationById(ids[4]));
        EXPECT_FALSE(message_center()->FindVisibleNotificationById(ids[5]));
        EXPECT_EQ(message_center()->GetVisibleNotifications().size(), 4u);
        message_center()->SetVisibility(VISIBILITY_TRANSIENT);

        EXPECT_TRUE(message_center()->FindVisibleNotificationById(ids[0]));
        EXPECT_FALSE(message_center()->FindVisibleNotificationById(ids[1]));
        EXPECT_TRUE(message_center()->FindVisibleNotificationById(ids[2]));
        EXPECT_TRUE(message_center()->FindVisibleNotificationById(ids[3]));
        EXPECT_TRUE(message_center()->FindVisibleNotificationById(ids[4]));
        EXPECT_TRUE(message_center()->FindVisibleNotificationById(ids[5]));
        EXPECT_EQ(message_center()->GetVisibleNotifications().size(), 5u);
    }

    TEST_F(MessageCenterImplTestWithChangeQueue, UpdateWhileQueueing)
    {
        std::string ids[11] = { "0", "1", "2", "3", "4", "5", "6", "7", "8", "9", "10p" };
        NotifierId notifier_id1(NotifierId::APPLICATION, "app1");

        scoped_ptr<Notification> notification;
        // Add some notifications
        int i = 0;
        for (; i < 6; i++) {
            notification.reset(CreateSimpleNotification(ids[i]));
            notification->set_title(base::ASCIIToUTF16("ORIGINAL TITLE"));
            message_center()->AddNotification(std::move(notification));
        }
        for (i = 0; i < 6; i++) {
            EXPECT_TRUE(message_center()->FindVisibleNotificationById(ids[i]));
        }
        for (; i < 8; i++) {
            EXPECT_FALSE(message_center()->FindVisibleNotificationById(ids[i]));
        }

        notification.reset(CreateNotification(ids[10], NOTIFICATION_TYPE_PROGRESS));
        notification->set_progress(10);
        message_center()->AddNotification(std::move(notification));

        // Now start queueing.
        message_center()->SetVisibility(VISIBILITY_MESSAGE_CENTER);

        // Notification 0: (Exists) -> Removed
        message_center()->RemoveNotification(ids[0], false);

        // Notification 1: (Exists) -> Removed (by user)
        message_center()->RemoveNotification(ids[1], true); // removed immediately

        // Notification 2: (Exists) -> Removed -> Added
        message_center()->RemoveNotification(ids[2], false);
        notification.reset(CreateSimpleNotification(ids[2]));
        notification->set_title(base::ASCIIToUTF16("NEW TITLE 2"));
        message_center()->UpdateNotification(ids[2], std::move(notification));

        // Notification 3: (Exists) -> Removed -> Updated
        message_center()->RemoveNotification(ids[3], false);
        notification.reset(CreateSimpleNotification(ids[3]));
        notification->set_title(base::ASCIIToUTF16("NEW TITLE 3"));
        message_center()->UpdateNotification(ids[3], std::move(notification));

        // Notification 4: (Exists) -> Removed -> Added -> Removed
        message_center()->RemoveNotification(ids[4], false);
        notification.reset(CreateSimpleNotification(ids[4]));
        message_center()->AddNotification(std::move(notification));
        message_center()->RemoveNotification(ids[4], false);

        // Notification 5: (Exists) -> Updated
        notification.reset(CreateSimpleNotification(ids[5]));
        notification->set_title(base::ASCIIToUTF16("NEW TITLE 5"));
        message_center()->UpdateNotification(ids[5], std::move(notification));

        // Notification 6: Updated
        notification.reset(CreateSimpleNotification(ids[6]));
        message_center()->UpdateNotification(ids[6], std::move(notification));

        // Notification 7: Updated -> Removed
        notification.reset(CreateSimpleNotification(ids[7]));
        message_center()->UpdateNotification(ids[7], std::move(notification));
        message_center()->RemoveNotification(ids[7], false);

        // Notification 8: Added -> Updated
        notification.reset(CreateSimpleNotification(ids[8]));
        notification->set_title(base::ASCIIToUTF16("UPDATING 8-1"));
        message_center()->AddNotification(std::move(notification));
        notification.reset(CreateSimpleNotification(ids[8]));
        notification->set_title(base::ASCIIToUTF16("NEW TITLE 8"));
        message_center()->UpdateNotification(ids[8], std::move(notification));

        // Notification 9: Added -> Updated -> Removed
        notification.reset(CreateSimpleNotification(ids[9]));
        message_center()->AddNotification(std::move(notification));
        notification.reset(CreateSimpleNotification(ids[9]));
        message_center()->UpdateNotification(ids[9], std::move(notification));
        message_center()->RemoveNotification(ids[9], false);

        // Notification 10 (TYPE_PROGRESS): Updated -> Removed -> Added -> Updated
        // Step 1) Progress is updated immediately before removed.
        notification.reset(CreateNotification(ids[10], NOTIFICATION_TYPE_PROGRESS));
        notification->set_progress(20);
        message_center()->UpdateNotification(ids[10], std::move(notification));
        EXPECT_EQ(20,
            message_center()->FindVisibleNotificationById(ids[10])->progress());
        message_center()->RemoveNotification(ids[10], false);
        // Step 2) Progress isn't updated after removed.
        notification.reset(CreateSimpleNotification(ids[10]));
        message_center()->AddNotification(std::move(notification));
        EXPECT_EQ(20,
            message_center()->FindVisibleNotificationById(ids[10])->progress());
        notification.reset(CreateSimpleNotification(ids[10]));
        notification->set_progress(40);
        message_center()->UpdateNotification(ids[10], std::move(notification));
        EXPECT_EQ(20,
            message_center()->FindVisibleNotificationById(ids[10])->progress());

        // All changes except the notification 1 and 10 are not refrected.
        EXPECT_TRUE(message_center()->FindVisibleNotificationById(ids[0]));
        EXPECT_EQ(base::ASCIIToUTF16("ORIGINAL TITLE"),
            message_center()->FindVisibleNotificationById(ids[0])->title());
        EXPECT_FALSE(message_center()->FindVisibleNotificationById(ids[1]));
        EXPECT_TRUE(message_center()->FindVisibleNotificationById(ids[2]));
        EXPECT_EQ(base::ASCIIToUTF16("ORIGINAL TITLE"),
            message_center()->FindVisibleNotificationById(ids[2])->title());
        EXPECT_TRUE(message_center()->FindVisibleNotificationById(ids[3]));
        EXPECT_EQ(base::ASCIIToUTF16("ORIGINAL TITLE"),
            message_center()->FindVisibleNotificationById(ids[3])->title());
        EXPECT_TRUE(message_center()->FindVisibleNotificationById(ids[4]));
        EXPECT_EQ(base::ASCIIToUTF16("ORIGINAL TITLE"),
            message_center()->FindVisibleNotificationById(ids[4])->title());
        EXPECT_TRUE(message_center()->FindVisibleNotificationById(ids[5]));
        EXPECT_EQ(base::ASCIIToUTF16("ORIGINAL TITLE"),
            message_center()->FindVisibleNotificationById(ids[5])->title());
        EXPECT_FALSE(message_center()->FindVisibleNotificationById(ids[6]));
        EXPECT_FALSE(message_center()->FindVisibleNotificationById(ids[7]));
        EXPECT_FALSE(message_center()->FindVisibleNotificationById(ids[8]));
        EXPECT_FALSE(message_center()->FindVisibleNotificationById(ids[9]));
        EXPECT_TRUE(message_center()->FindVisibleNotificationById(ids[10]));
        EXPECT_EQ(message_center()->GetVisibleNotifications().size(), 6u);

        message_center()->SetVisibility(VISIBILITY_TRANSIENT);

        EXPECT_FALSE(message_center()->FindVisibleNotificationById(ids[0]));
        EXPECT_FALSE(message_center()->FindVisibleNotificationById(ids[1]));
        EXPECT_TRUE(message_center()->FindVisibleNotificationById(ids[2]));
        EXPECT_EQ(base::ASCIIToUTF16("NEW TITLE 2"),
            message_center()->FindVisibleNotificationById(ids[2])->title());
        EXPECT_TRUE(message_center()->FindVisibleNotificationById(ids[3]));
        EXPECT_EQ(base::ASCIIToUTF16("NEW TITLE 3"),
            message_center()->FindVisibleNotificationById(ids[3])->title());
        EXPECT_FALSE(message_center()->FindVisibleNotificationById(ids[4]));
        EXPECT_TRUE(message_center()->FindVisibleNotificationById(ids[5]));
        EXPECT_EQ(base::ASCIIToUTF16("NEW TITLE 5"),
            message_center()->FindVisibleNotificationById(ids[5])->title());
        EXPECT_FALSE(message_center()->FindVisibleNotificationById(ids[6]));
        EXPECT_FALSE(message_center()->FindVisibleNotificationById(ids[7]));
        EXPECT_TRUE(message_center()->FindVisibleNotificationById(ids[8]));
        EXPECT_EQ(base::ASCIIToUTF16("NEW TITLE 8"),
            message_center()->FindVisibleNotificationById(ids[8])->title());
        EXPECT_FALSE(message_center()->FindVisibleNotificationById(ids[9]));
        EXPECT_TRUE(message_center()->FindVisibleNotificationById(ids[10]));
        EXPECT_EQ(40,
            message_center()->FindVisibleNotificationById(ids[10])->progress());
        EXPECT_EQ(message_center()->GetVisibleNotifications().size(), 5u);
    }

    TEST_F(MessageCenterImplTestWithChangeQueue, QueuedDirectUpdates)
    {
        std::string id("id1");
        std::string id2("id2");
        NotifierId notifier_id1(NotifierId::APPLICATION, "app1");

        gfx::Size original_size(0, 0);
        // Open the message center to prevent adding notifications
        message_center()->SetVisibility(VISIBILITY_MESSAGE_CENTER);

        // Create new notification to be added to the queue; images all have the same
        // original size.
        scoped_ptr<Notification> notification(CreateSimpleNotification(id));

        // Double-check that sizes all match.
        const std::vector<ButtonInfo>& original_buttons = notification->buttons();
        ASSERT_EQ(2u, original_buttons.size());

        EXPECT_EQ(original_size, notification->icon().Size());
        EXPECT_EQ(original_size, notification->image().Size());
        EXPECT_EQ(original_size, original_buttons[0].icon.Size());
        EXPECT_EQ(original_size, original_buttons[1].icon.Size());

        message_center()->AddNotification(std::move(notification));

        // The notification should be in the queue.
        EXPECT_FALSE(message_center()->FindVisibleNotificationById(id));

        // Now try setting the icon to a different size.
        gfx::Size new_size(16, 16);
        EXPECT_NE(original_size, new_size);

        gfx::Canvas canvas(new_size, 1.0f, true);
        canvas.DrawColor(SK_ColorBLUE);
        gfx::Image testImage(gfx::Image(gfx::ImageSkia(canvas.ExtractImageRep())));
        message_center()->SetNotificationIcon(id, testImage);
        message_center()->SetNotificationImage(id, testImage);
        message_center()->SetNotificationButtonIcon(id, 0, testImage);
        message_center()->SetNotificationButtonIcon(id, 1, testImage);

        // The notification should be in the queue.
        EXPECT_FALSE(message_center()->FindVisibleNotificationById(id));

        // Close the message center; then the update should have propagated.
        message_center()->SetVisibility(VISIBILITY_TRANSIENT);
        // The notification should no longer be in the queue.
        EXPECT_TRUE(message_center()->FindVisibleNotificationById(id));

        Notification* mc_notification = *(message_center()->GetVisibleNotifications().begin());
        const std::vector<ButtonInfo>& buttons = mc_notification->buttons();
        ASSERT_EQ(2u, buttons.size());

        EXPECT_EQ(new_size, mc_notification->icon().Size());
        EXPECT_EQ(new_size, mc_notification->image().Size());
        EXPECT_EQ(new_size, buttons[0].icon.Size());
        EXPECT_EQ(new_size, buttons[1].icon.Size());
    }

    TEST_F(MessageCenterImplTestWithChangeQueue, ForceNotificationFlushAdd)
    {
        std::string id("id1");

        message_center()->SetVisibility(VISIBILITY_MESSAGE_CENTER);
        message_center()->AddNotification(
            scoped_ptr<Notification>(CreateSimpleNotification(id)));

        // Notification is not added yet.
        ASSERT_EQ(0u, message_center()->NotificationCount());
        // Forced to update.
        ForceNotificationFlush(id);
        // Notification is added.
        ASSERT_EQ(1u, message_center()->NotificationCount());
    }

    TEST_F(MessageCenterImplTestWithChangeQueue, ForceNotificationFlushUpdate)
    {
        std::string id("id1");
        std::string id2("id2");

        scoped_ptr<Notification> notification(CreateSimpleNotification(id));
        message_center()->AddNotification(std::move(notification));

        message_center()->SetVisibility(VISIBILITY_MESSAGE_CENTER);

        notification.reset(CreateSimpleNotification(id2));
        message_center()->UpdateNotification(id, std::move(notification));

        // Nothing is changed.
        EXPECT_FALSE(message_center()->FindVisibleNotificationById(id2));
        EXPECT_TRUE(message_center()->FindVisibleNotificationById(id));

        // Forced to update ID1.
        ForceNotificationFlush(id);

        // Nothing is changed, since the ID is changed.
        EXPECT_FALSE(message_center()->FindVisibleNotificationById(id2));
        EXPECT_TRUE(message_center()->FindVisibleNotificationById(id));

        // Forced to update ID2.
        ForceNotificationFlush(id2);

        // The ID is changed.
        EXPECT_TRUE(message_center()->FindVisibleNotificationById(id2));
        EXPECT_FALSE(message_center()->FindVisibleNotificationById(id));

        // Makes sure if there is only one notification.
        ASSERT_EQ(1u, message_center()->NotificationCount());
    }

    TEST_F(MessageCenterImplTestWithChangeQueue, ForceNotificationFlushRemove)
    {
        std::string id("id1");

        scoped_ptr<Notification> notification(CreateSimpleNotification(id));
        message_center()->AddNotification(std::move(notification));

        message_center()->SetVisibility(VISIBILITY_MESSAGE_CENTER);
        message_center()->RemoveNotification(id, false);

        // Notification is not removed yet.
        ASSERT_EQ(1u, message_center()->NotificationCount());

        // Forced to update.
        ForceNotificationFlush(id);

        // Notification is removed.
        ASSERT_EQ(0u, message_center()->NotificationCount());
    }

    TEST_F(MessageCenterImplTestWithChangeQueue,
        ForceNotificationFlush_ComplexUpdate)
    {
        std::string id1("id1");
        std::string id2("id2");
        std::string id3("id3");

        message_center()->SetVisibility(VISIBILITY_MESSAGE_CENTER);

        // Add -> Update (with ID change) -> Remove
        scoped_ptr<Notification> notification(CreateSimpleNotification(id1));
        message_center()->AddNotification(std::move(notification));
        notification.reset(CreateSimpleNotification(id2));
        message_center()->UpdateNotification(id1, std::move(notification));
        message_center()->RemoveNotification(id2, false);

        // Add -> Update (with ID change)
        notification.reset(CreateSimpleNotification(id2));
        message_center()->AddNotification(std::move(notification));
        notification.reset(CreateSimpleNotification(id3));
        message_center()->UpdateNotification(id2, std::move(notification));

        // Notification is not added since the message center has opened.
        ASSERT_EQ(0u, message_center()->NotificationCount());

        // Forced to update.
        ForceNotificationFlush(id3);

        // Confirms the chagnes are applied.
        ASSERT_EQ(1u, message_center()->NotificationCount());
    }

    TEST_F(MessageCenterImplTestWithoutChangeQueue,
        UpdateWhileMessageCenterVisible)
    {
        std::string id("id1");
        std::string id2("id2");
        NotifierId notifier_id1(NotifierId::APPLICATION, "app1");

        // First, add and update a notification to ensure updates happen
        // normally.
        scoped_ptr<Notification> notification(CreateSimpleNotification(id));
        message_center()->AddNotification(std::move(notification));
        notification.reset(CreateSimpleNotification(id2));
        message_center()->UpdateNotification(id, std::move(notification));
        EXPECT_TRUE(message_center()->FindVisibleNotificationById(id2));
        EXPECT_FALSE(message_center()->FindVisibleNotificationById(id));

        // Then open the message center.
        message_center()->SetVisibility(VISIBILITY_MESSAGE_CENTER);

        // Then update a notification; the update should have propagated.
        notification.reset(CreateSimpleNotification(id));
        message_center()->UpdateNotification(id2, std::move(notification));
        EXPECT_FALSE(message_center()->FindVisibleNotificationById(id2));
        EXPECT_TRUE(message_center()->FindVisibleNotificationById(id));
    }

    TEST_F(MessageCenterImplTestWithoutChangeQueue, AddWhileMessageCenterVisible)
    {
        std::string id("id1");

        // Then open the message center.
        message_center()->SetVisibility(VISIBILITY_MESSAGE_CENTER);

        // Add a notification and confirm the adding should have propagated.
        scoped_ptr<Notification> notification(CreateSimpleNotification(id));
        message_center()->AddNotification(std::move(notification));
        EXPECT_TRUE(message_center()->FindVisibleNotificationById(id));
    }

    TEST_F(MessageCenterImplTestWithoutChangeQueue,
        RemoveWhileMessageCenterVisible)
    {
        std::string id("id1");

        // First, add a notification to ensure updates happen normally.
        scoped_ptr<Notification> notification(CreateSimpleNotification(id));
        message_center()->AddNotification(std::move(notification));
        EXPECT_TRUE(message_center()->FindVisibleNotificationById(id));

        // Then open the message center.
        message_center()->SetVisibility(VISIBILITY_MESSAGE_CENTER);

        // Then update a notification; the update should have propagated.
        message_center()->RemoveNotification(id, false);
        EXPECT_FALSE(message_center()->FindVisibleNotificationById(id));
    }

} // namespace internal
} // namespace message_center
